package com.team5101.bootstrap;


import com.team5101.configEntity.RpcProperties;
import com.team5101.encode.RpcDecoder;
import com.team5101.hander.DemoServiceHandler;
import com.team5101.model.RpcRequest;
import com.team5101.serializer.JSONSerializer;
import com.team5101.util.SpringBeanUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class ServerProvoiderBootStrap {


    @Autowired
    private SpringBeanUtil springBeanUtil;

    @Autowired
    private RpcProperties rpcProperties;

    @Autowired
    private CuratorFramework curatorFramework;

    public void startServer() throws InterruptedException {
        log.info("服务提供方启动 provider ...");

        //1.创建NioEventSocketLoopGroup
        //接受请求
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //处理请求
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //2创建服务启动辅助类,装配组件
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                //指定服务器端监听套接字通道NioServerSocketChannel
                .channel(NioServerSocketChannel.class)
                //设置业务职责链channelHandler组成
                //ChannelInitializer:初始化channel的channelPipelline
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();

                        //解决粘包/半包,根据消息长度自动拆包
                        /**
                         *TCP粘包和黏包现象
                         *   1. TCP粘包是指发送方发送的若干个数据包到接收方时粘成一个包。从接收缓冲区来看，后一个包数据的头紧接着前一个数据的尾。
                         *   2. 当TCP连接建立后，Client发送多个报文给Server，TCP协议保证数据可靠性，但无法保证Client发了n个包，服务端也按照n个包接收。Client端发送n个数据包，Server端可能收到n-1或n+1个包。
                         *
                         *为什么出现粘包现象
                         *   1. 发送方原因: TCP默认会使用Nagle算法。而Nagle算法主要做两件事：1）只有上一个分组得到确认，才会发送下一个分组；2）收集多个小分组，在一个确认到来时一起发送。所以，正是Nagle算法造成了发送方有可能造成粘包现象。
                         *   2. 接收方原因: TCP接收方采用缓存方式读取数据包，一次性读取多个缓存中的数据包。自然出现前一个数据包的尾和后一个收据包的头粘到一起。
                         *
                         *如何解决粘包现象
                         * 1. 添加特殊符号，接收方通过这个特殊符号将接收到的数据包拆分开 - DelimiterBasedFrameDecoder特殊分隔符解码器
                         * 2. 每次发送固定长度的数据包 - FixedLengthFrameDecoder定长编码器
                         * 3. 在消息头中定义长度字段，来标识消息的总长度 - LengthFieldBasedFrameDecoder自定义长度解码器
                         */

                        // 解决粘包现象
                        pipeline.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));

                        //将channelHandler添加到链上
                        //在请求近来时和出去时候都会经过链
                        pipeline.addLast(new RpcDecoder(RpcRequest.class, new JSONSerializer()));

                        //pipeline.addLast(new RpcEncoder(RpcResponse.class, new JSONSerializer()));
                        pipeline.addLast(new StringEncoder());

//                        pipeline.addLast(new DemoServiceHandler(springBeanUtil));
                        pipeline.addLast(new DemoServiceHandler(curatorFramework, springBeanUtil, rpcProperties));

                    }
                });

        //3 绑定端口，sync():用户阻塞当前线程，直到端口绑定完成
        ChannelFuture channelFuture = serverBootstrap.bind(rpcProperties.getHost(), rpcProperties.getPort()).sync();
        log.info("producer 注册信息：host: {} ，port: {}", rpcProperties.getHost(), rpcProperties.getPort());
        log.info("RPC Server start success ...");

        //应用程序阻塞，直到应用程序关闭
        channelFuture.channel().closeFuture();
    }

}
